/*
 * chapter_16.cpp
 *   模板与泛型编程
 *  Created on: 2020年12月13日
 *      Author: san
 */

#include <vector>
#include <list>
#include <sstream>

#include "common_tools.h"
#include "Sales_data.h"
#include "BlobPtr.h"
#include "MyVec.h"  // include 名字一样导致的错误，上一个是

void t16_1()
{
	std::cout << san::compare(10, 2) << std::endl;
	std::cout << san::compare(Sales_data("Y0001"), Sales_data("Y0002")) << std::endl;
}

void t16_4()
{
	std::vector<int> ivec{0,1,2,3,4,5,5,6,43};
	std::list<int> ilst{9,8,7,6,5,4,3,2,1};
	auto vr = san::find(ivec.begin(), ivec.end(), 3);
	auto lr = san::find(ilst.begin(), ilst.end(), 6);

	if (vr != ivec.end())
		std::cout << "vr: " << *vr << std::endl;
	if (lr != ilst.end())
		std::cout << "lr: " << *lr << std::endl;
}

void print_arr(int (&array)[10])
{
	for (const auto &item: array)
	{
		std::cout << item << " ";
	}
}

void t16_5()
{
	int arr[10] {0,1,2,3,4,5,6};
	san::print(arr);
	std::cout << std::endl;
	print_arr(arr);
}

void t16_7()
{
	int arr[10]{0};
	std::cout << "size() of arr: " << san::size(arr) << std::endl;;
}

void test_static()
{
	// 因为两个文件相同的文件名
	// 测试static
//	 Foo1 = san::Foo1<int>;
//	san::Foo1 fi;
//	std::cout << Foo<std::string>::test << std::endl;
}

void t16_16()
{
	std::cout << "t16_16()" << std::endl;
	std::vector<san::MyVec<int>> mvec;
	mvec.reserve(2);
	san::MyVec<int> svec;
	svec.push_back(1);
	for (size_t i = 0; i < 3; ++i)
	{
		mvec.push_back(svec);  // 调用移动构造函数之后就会出现delete两次的情况
	}
}

template <typename T> T fobj(T a, T b) { return a; };
template <typename T> void fref(const T &a, const T &b) { };

void e16_2_1()
{

	std::string s1("a value");
	const std::string s2("another value");

	int a[10], b[10];  // 这两个长度一样才行
	fref(a, b);
	std::cout << "Ok" << std::endl;
}

void t16_40()
{
	std::vector<int> ivec{0,1,2,3,4,5,5,6,5,6,7};
	std::cout << san::fcn3(ivec.begin(), ivec.end(), 5) << std::endl;;
}

void t16_41()
{
	auto res = san::sum(1, 2);
	std::cout << res << std::endl;
}

void func(int(*)(const int &, const int &)) {}

void e16_2_4()
{
	// 模板指针和实参推断
	int (*pf1)(const int &, const int &) = san::sum;
	void func(int(*)(const int &, const int &));
	func(pf1);
	func(san::sum<int, int>);
}

void f16_2(const int &i)
{
	std::cout << i << std::endl;
}

void e16_2_5()
{
	// 模板指针推断和引用
	f16_2(1);
}

template <typename T> void g(T &&val) { }
void t16_42()
{
	int *i = 0;
	g(std::move(i));
//	std::cout << *i << std::endl;
	std::cout << "ok" << std::endl;
}

void f(int v1, int &v2)
{
	std::cout << " " << ++v2 << std::endl;
}

void t16_47()
{
	int i = 0;
	// 测试转发
	san::flip(f, i, 21);
}

template <typename T>
std::string debeg(const T &t)
{
	std::ostringstream ret;
	ret << t;
	return ret.str();
}

template <typename T>
std::string debeg(const T *p)
{
	std::ostringstream ret;
	ret << "pointer: " << p;
	if (p)
		ret << " " << debeg(*p);
	else
		ret << " null pointer";  // 或指出p为空
	return ret.str();
}

template <typename T> void f(T) {std::cout << "f(T)" << std::endl;};
template <typename T> void f(const T *) { std::cout << "f(const T *)" << std::endl; };

void t16_50()
{
	int i = 42, *p = &i;
	f(p);
}

std::string debeg(const std::string &t)
{
	std::ostringstream ret;
	ret << t;
	return ret.str();
}

template <typename T, typename... Args>
void foo(const T &, const Args... args)
{
	std::cout << sizeof...(Args) << std::endl;
	std::cout << sizeof...(args) << std::endl;
}

void t16_52()
{
	int i = 0;
	double d = 3.14;
	foo(i, d, "hello", 3.142, 3.142);
}

struct T16_54 {
	int i = 0;
};

template <typename T>
ostream &print_args(ostream &os, const T &t)
{
	return os << t;  // 包中最后一个元素之后不打印分割符
}

template <typename T> void print(ostream &os, const T &t);

template <typename T, typename... Args>
ostream &print_args(ostream &os, const T &t, const Args &... rest)
{
	os << t << ", ";
	return print_args(os, rest...);
}

void t16_54()
{
	print_args(std::cout, 1, "hello");
}

template <typename... Args>
ostream &errmsg(ostream &os, const Args&... rest)
{
	return print_args(os, debeg(rest)...);  // print_args
}

void t16_56()
{
	errmsg(std::cerr, "hello", "cpp", "hello", "java");
}

void t16_58()
{
	// 为MyVec添加emplace_back
	san::MyVec<std::string> svec;
//	svec.emplace_back("hello world!");
	string s1("hello world!");
	string s2("hello world!");
	string s3("hello world!");
	svec.emplace_back("hello");  // 这里还是不能写几个参数阿
	san::print(svec.begin(), svec.end());
}

int main16()
{
	std::cout << "---------------函数模板-----------------" << std::endl;
	t16_1();
	t16_4();
	t16_5();
	std::cout << std::endl;
	t16_7();

	std::cout << "---------------类模板(Blob/BlobPtr)-----------------" << std::endl;
	san::Blob<int> ib({0,1,2,3,4,5,9});
	san::Blob<std::string> sb({"hello world", "hello c", "hello java"});
	ib.pop_back();
	san::print(sb.begin(), sb.end());

	t16_16();  // 移动构造里移动完后需要将指针置为 nullptr

	std::cout << "---------------模板实参推断-----------------" << std::endl;
	e16_2_1();
	std::cout << std::max(1, 2) << std::endl;
	t16_40();
	t16_42();  // 引用

	std::cout << "----------------转发---------------" << std::endl;
	t16_47();
	t16_50();

	std::cout << "----------------可变参数模板---------------" << std::endl;
	t16_52();
	t16_54();
//	t16_56();

	std::cout << "------------------转发参数包--------------------" << std::endl;
	t16_58();

	return 0;
}

